package com.cloud.advice;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

@Slf4j
@Aspect
@Component
public class PageAdvice {

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Pointcut("execution(* com.cloud.dao.*.*Page(..))")
    public void execute() {
    }

    @Around("execute()")
    public Object doAround(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();
        Integer pageNum = (Integer) args[args.length - 2];
        Integer pageSize = (Integer) args[args.length - 1];
        if (pageNum == null || pageSize == null) {
            pageNum = 1;
            pageSize = 10;
        }
        PageHelper.startPage(pageNum, pageSize);
        Class targetProxyInterfaceType = pjp.getStaticPart().getSignature().getDeclaringType();//获取被代理目标接口
        String targetProxyMethodName = pjp.getStaticPart().getSignature().getName();//获取被代理方法名称
        Method targetProxyMethod = null;//获取被代理方法
        for (Method method : targetProxyInterfaceType.getDeclaredMethods()) {
            if (method.getName().equals(targetProxyMethodName)) {
                targetProxyMethod = method;
                continue;
            }
        }
        Configuration configuration = sqlSessionFactory.getConfiguration();//Mybatis 全局配置
        MapperMethod mapperMethod = new MapperMethod(targetProxyInterfaceType, targetProxyMethod, configuration);

        //以下是关键  java通过反射修改MapperMethod对象属性，绕过检测机制，切记请勿修改全局配置configuration 对象的属性
        SqlSession sqlSession = null;
        PageInfo pageInfo = null;
        try {
            Field methodSignature = mapperMethod.getClass().getDeclaredField("method");
            methodSignature.setAccessible(true);
            Object o = methodSignature.get(mapperMethod);//methodField 实例
            Class<?> type = methodSignature.getType();//methodField class对象

            //需要修改的第一个属性
            Field returnsMany = type.getDeclaredField("returnsMany");//具体字段
            Field modifiersField = Field.class.getDeclaredField("modifiers");//对字段再次进行反射（两次反射）达到修改final变量的目的
            modifiersField.setAccessible(true);
            modifiersField.setInt(returnsMany, returnsMany.getModifiers() & ~Modifier.FINAL);
            returnsMany.setAccessible(true);//修改完了还要将原始的访问字段设置为true
            returnsMany.set(o, true);

            //需要修改的第二个属性
            Field returnType = type.getDeclaredField("returnType");//具体字段
            Field modifiersField2 = Field.class.getDeclaredField("modifiers");
            modifiersField2.setAccessible(true);
            modifiersField2.setInt(returnType, returnType.getModifiers() & ~Modifier.FINAL);
            returnType.setAccessible(true);
            returnType.set(o, com.github.pagehelper.Page.class);
            sqlSession = sqlSessionFactory.openSession();
            Object execute = mapperMethod.execute(sqlSession, args);
            pageInfo = new PageInfo((List) execute);
        } catch (Exception e) {
            e.printStackTrace();
            log.debug("反射修改MapperMethod属性出错：", e);
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }
        return pageInfo;
    }

}
